package common

import (
	"context"
	"fmt"
	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/stores/redis"
	"time"
)

// Lock redis lock
func Lock(cli *redis.Redis, key string, fn func()) {
	lock := redis.NewRedisLock(cli, key)
	lock.SetExpire(15)
	acquire, err := lock.Acquire()
	switch {
	case err != nil:
		logx.Error(err)
	case acquire:
		defer lock.Release()
		fn()
	case !acquire:
		logx.Error("lock failed")
	}
}

// LockWithExpire redis lock with expire
func LockWithExpire(cli *redis.Redis, key string, expire int, fn func()) {
	lock := redis.NewRedisLock(cli, key)
	lock.SetExpire(expire)
	acquire, err := lock.Acquire()
	switch {
	case err != nil:
		logx.Error(err)
	case acquire:
		defer lock.Release()
		fn()
	case !acquire:
		logx.Error("lock failed")
	}
}

// AcquireLockWithExpire 获取 Redis 分布式锁，并设置超时时间
func AcquireLockWithExpire(cli *redis.Redis, key string, expire int) error {
	redisLock := redis.NewRedisLock(cli, key)
	redisLock.SetExpire(expire)
	ok, err := redisLock.Acquire()
	if err != nil {
		return fmt.Errorf("failed to acquire lock: %v", err)
	}
	if !ok {
		return fmt.Errorf("failed to acquire lock")
	}
	return nil
}

// ReleaseLock 释放 Redis 分布式锁
func ReleaseLock(cli *redis.Redis, key string) error {
	redisLock := redis.NewRedisLock(cli, key)
	_, err := redisLock.Release()
	if err != nil {
		logx.Errorf("failed to release lock: %v", err)
		return err
	}
	return nil
}

// AcquireLockWithExpireWithContext 获取 Redis 分布式锁，并设置超时时间，如果锁未释放则等待
func AcquireLockWithExpireWithContext(ctx context.Context, cli *redis.Redis, key string, expire int, retryInterval time.Duration, maxRetries int) error {
	for i := 0; i < maxRetries; i++ {
		redisLock := redis.NewRedisLock(cli, key)
		redisLock.SetExpire(expire)
		ok, err := redisLock.Acquire()
		if err != nil {
			return fmt.Errorf("failed to acquire lock: %v", err)
		}
		if ok {
			return nil // 获取锁成功
		}

		// 获取锁失败，等待一段时间后重试
		select {
		case <-ctx.Done():
			return ctx.Err() // 上下文取消，退出等待
		case <-time.After(retryInterval):
			// 使用指数退避算法增加重试间隔
			retryInterval = time.Duration(float64(retryInterval) * 1.5)
		}
	}

	return fmt.Errorf("failed to acquire lock after %d retries", maxRetries)
}
